package spitfire.ksim.algorithm;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SimilarityAlgorithm {

	private static double calMean(List<Double> values) {
		double sum = 0;;
		for (double d : values) {
			sum += d;
		}
		
		return sum / values.size();
	}
	
	private static double calDeviation(List<Double> values, double mean) {
		double sum = 0;
		for (double d : values) {
			sum += (d - mean) * (d - mean);
		}
		return Math.sqrt(sum / values.size());
	}
	
	public static double calculateScore(
			List<Long> timeList,
			List<Double> dataList,
			FuzzyRule timeRule,
			FuzzyRule valueRule) {
		if (valueRule.size() < 2) {
			throw new RuntimeException("size too small");
		}
//		double rawMax = Collections.max(dataList);
//		double rawMin = Collections.min(dataList);
		double mean = calMean(dataList);
		double deviation = calDeviation(dataList, mean);
		double rawMax = mean + 3 * deviation;
		double rawMin = mean - 3 * deviation;
		
		List<Double> timeDiffList = FuzzyRuleBase.getTimeDiffList(timeList);
		double timeDiffMean = calMean(timeDiffList);
		double timeDiffDeviation = calDeviation(timeDiffList, timeDiffMean);
		double timeDiffMax = timeDiffMean + 3 * timeDiffDeviation;
		double timeDiffMin = timeDiffMean - 3 * timeDiffDeviation;
		
		ArrayList<Double> xList = valueRule.getxList();
		ArrayList<Double> yList = valueRule.getyList();
		double ruleMax = valueRule.getrMax();
		double ruleMin = valueRule.getrMin();
		
		double drange = Math.abs(rawMin-ruleMin) + Math.abs(rawMax-ruleMax);
		
		if (dataList.size() - timeDiffList.size() != 1) {
			throw new RuntimeException();
		}
		
		double sc = 0;
		for (int i = 1; i < dataList.size(); i++) {
			double dataValue = dataList.get(i);
			double timeDiff = timeDiffList.get(i - 1);
			
			// omit outliers
			if (dataValue < rawMin || dataValue > rawMax) {
				continue;
			}
			
			if (timeDiff < timeDiffMin || timeDiff > timeDiffMax) {
				continue;
			}
				
			sc += interpolate(xList, yList, dataValue)
					* interpolate(timeRule.getxList(), timeRule.getyList(), timeDiff);
		}
//		sc /= drange;
		return sc;
	}
	
	public static double interpolate(List<Double> xList, List<Double> yList, double pointX) {
		int p1 = Collections.binarySearch(xList, pointX);
		int p2 = 0;
		if (p1 >= 0 ) {
			// found
			return yList.get(p1);
		} else {
			// not found
			p1 = -p1 - 1;
			if (p1 == 0) {
				// smaller than min
				return 0;
			} else if (p1 == xList.size()) {
				// bigger than max
//				p1 = p1 - 2;
				return 0;
			} else {
				// data value between rule range
				p1--;
				p2 = p1 + 1;
				double x1 = xList.get(p1);
				double x2 = xList.get(p2);
				double y1 = yList.get(p1);
				double y2 = yList.get(p2);
				return (x1*y2-y1*x2)/(x1-x2) + (y2-y1)/(x2-x1)*pointX;
			}
		}
	}
}
